package com.bi.cloud.config;

import com.bi.cloud.service.AuthUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.JdbcClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.InMemoryApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableAuthorizationServer
public class OAuthConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    public PasswordEncoder passwordEncoder;

    @Autowired
    public AuthUserService userDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    @Autowired
    private TokenStore jwtTokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private TokenEnhancer jwtTokenEnhancer;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Resource
    private DataSource dataSource;

    //@Autowired
    //private AuthorizationServerTokenServices tokenServices;

    //配置令牌端点的安全约束
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

        security.tokenKeyAccess("permitAll()")
                // 第三方客户端校验token需要带入 clientId 和clientSecret来校验
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }


    //配置令牌的访问端点:需要配置认证管理器,授权码服务,令牌管理服务,令牌的请求方式
    //这几个都需要以bean的形式进行配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager)//认证管理器,密码模式时使用
                    .authorizationCodeServices(authorizationCodeServices) // 授权码服务，授权码模式时使用,针对授权码模式有效，会将授权码放到 auth_code 表，授权后就会删除它
                    //.tokenServices(tokenServices) // 令牌管理
                    .tokenStore(jwtTokenStore) // 基于jwt管理token
                    .accessTokenConverter(jwtAccessTokenConverter)
                    .tokenEnhancer(tokenEnhancerChain()) // 添加增强jwt
                    .userDetailsService(userDetailsService) // 刷新令牌授权包含对用户信息的检查
                    .approvalStore(approvalStore())  // 授权记录
                    .allowedTokenEndpointRequestMethods(HttpMethod.POST);//允许以POST方式获取令牌
    }
    //配置客户端
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 从数据库中获取客户端信息
        clients.withClientDetails(clientDetails());
    }

    /**
     * 从数据库中获取客户端详情
     * @return
     */
    public ClientDetailsService clientDetails() {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);
        return jdbcClientDetailsService;
    }


    // 配置授权码储存，可存于内存与数据库中
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(){
        // 授权码存于数据库
        return new JdbcAuthorizationCodeServices(dataSource);
    }
    // 配置管理令牌服务 ，DefaultTokenServices 和 RemoteTokenServices
    /*@Bean
    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setClientDetailsService(clientDetailsService);//客户端详情,从容器中获取
        tokenServices.setTokenStore(jwtTokenStore);//基于redis存储令牌，管理token的方式

        tokenServices.setTokenEnhancer(tokenEnhancerChain());  //添加增强jwt
        // 是否支持 refreshToken
        tokenServices.setSupportRefreshToken(true);
        // 是否复用 refreshToken
        tokenServices.setReuseRefreshToken(false);
        // token有效期自定义设置，默认12小时
        tokenServices.setAccessTokenValiditySeconds(60 * 60 * 24);
        //默认30天，这里修改
        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
        return tokenServices;
    }*/

    /**
     * 增强JWT
     * @return
     */
    public TokenEnhancerChain tokenEnhancerChain() {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancers = new ArrayList<>();
        enhancers.add(jwtAccessTokenConverter);
        enhancers.add(jwtTokenEnhancer);
        //将自定义Enhancer加入EnhancerChain的delegates数组中
        enhancerChain.setTokenEnhancers(enhancers);
        return enhancerChain;
    }

    /**
     * 使用使用内存方式来保存用户的授权批准记录，也可以使用jdbc保存
     * @return
     */
    @Bean
    public ApprovalStore approvalStore(){
        // 授权码存储数据库
        return new JdbcApprovalStore(dataSource);
    }
}
